💾 Image Sequence Saver¶
Documentation¶
- Class name:
Image Sequence Saver [Dream]
- Category:
✨ Dream/🌄 image/🎥 animation
- Output node:
True
The Image Sequence Saver node is designed to save individual frames of an animation sequence to disk, supporting various file formats and configurations. It allows for detailed control over the output naming convention, directory structure, and file format, facilitating the creation of organized and accessible animation frame libraries.
Input types¶
Required¶
frame_counter
- Specifies the current frame in the animation sequence, used to determine the filename and whether the saving process should continue based on the 'at_end' parameter.
- Comfy dtype:
FRAME_COUNTER
- Python dtype:
FrameCounter
image
- The image to be saved for the current frame, representing a single snapshot in the animation sequence.
- Comfy dtype:
IMAGE
- Python dtype:
DreamImage
directory_path
- The target directory path where the image file will be saved, allowing for organized storage of animation frames.
- Comfy dtype:
STRING
- Python dtype:
str
prefix
- A prefix added to the filename of each saved image, aiding in the identification and organization of frames within the directory.
- Comfy dtype:
STRING
- Python dtype:
str
digits
- The number of digits to use for zero-padding the frame number in the filename, ensuring a consistent file naming convention.
- Comfy dtype:
INT
- Python dtype:
int
at_end
- Determines the behavior when the end of the animation sequence is reached, offering options to stop output, raise an error, or keep going.
- Comfy dtype:
COMBO[STRING]
- Python dtype:
str
filetype
- Specifies the file format for the saved image, including options for PNG with or without embedded workflow information, and JPG.
- Comfy dtype:
COMBO[STRING]
- Python dtype:
str
Output types¶
sequence
- Comfy dtype:
ANIMATION_SEQUENCE
- The updated animation sequence after saving the current frame, reflecting any changes or additions.
- Python dtype:
AnimationSequence.ID
- Comfy dtype:
log_entry
- Comfy dtype:
LOG_ENTRY
- A log entry detailing the outcome of the save operation, including success messages or error information.
- Python dtype:
LogEntry.ID
- Comfy dtype:
Usage tips¶
- Infra type:
CPU
- Common nodes: unknown
Source code¶
class DreamImageSequenceOutput:
NODE_NAME = "Image Sequence Saver"
ICON = "💾"
@classmethod
def INPUT_TYPES(cls):
return {
"required": SharedTypes.frame_counter | {
"image": ("IMAGE",),
"directory_path": ("STRING", {"default": comfy_paths.output_directory, "multiline": False}),
"prefix": ("STRING", {"default": 'frame', "multiline": False}),
"digits": ("INT", {"default": 5}),
"at_end": (["stop output", "raise error", "keep going"],),
"filetype": (['png with embedded workflow', "png", 'jpg'],),
},
"hidden": {
"prompt": "PROMPT",
"extra_pnginfo": "EXTRA_PNGINFO"
},
}
CATEGORY = NodeCategories.IMAGE_ANIMATION
RETURN_TYPES = (AnimationSequence.ID, LogEntry.ID)
OUTPUT_NODE = True
RETURN_NAMES = ("sequence", "log_entry")
FUNCTION = "save"
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
def _get_new_filename(self, current_frame, prefix, digits, filetype):
return prefix + "_" + str(current_frame).zfill(digits) + "." + filetype.split(" ")[0]
def _save_single_image(self, dream_image: DreamImage, batch_counter, frame_counter: FrameCounter,
directory_path,
prefix, digits, filetype, prompt, extra_pnginfo, at_end, logger):
if at_end == "stop output" and frame_counter.is_after_last_frame:
logger("Reached end of animation - not saving output!")
return ()
if at_end == "raise error" and frame_counter.is_after_last_frame:
logger("Reached end of animation - raising error to stop processing!")
raise Exception("Reached end of animation!")
filename = self._get_new_filename(frame_counter.current_frame, prefix, digits, filetype)
if batch_counter >= 0:
filepath = os.path.join(directory_path, "batch_" + (str(batch_counter).zfill(4)), filename)
else:
filepath = os.path.join(directory_path, filename)
save_dir = os.path.dirname(filepath)
if not os.path.isdir(save_dir):
os.makedirs(save_dir)
if filetype.startswith("png"):
dream_image.save_png(filepath, filetype == 'png with embedded workflow', prompt, extra_pnginfo)
elif filetype == "jpg":
dream_image.save_jpg(filepath, int(CONFIG.get("encoding.jpeg_quality", 95)))
logger("Saved {} in {}".format(filename, os.path.abspath(save_dir)))
return ()
def _generate_animation_sequence(self, filetype, directory_path, frame_counter):
if filetype.startswith("png"):
pattern = "*.png"
else:
pattern = "*.jpg"
frames = list_images_in_directory(directory_path, pattern, False)
return AnimationSequence(frame_counter, frames)
def save(self, image, **args):
log_texts = list()
logger = lambda s: log_texts.append(s)
if not args.get("directory_path", ""):
args["directory_path"] = comfy_paths.output_directory
args["logger"] = logger
proc = DreamImageProcessor(image, **args)
proc.process(self._save_single_image)
frame_counter = args["frame_counter"]
log_entry = LogEntry([])
for text in log_texts:
log_entry = log_entry.add(text)
if frame_counter.is_final_frame:
return (self._generate_animation_sequence(args["filetype"], args["directory_path"],
frame_counter), log_entry)
else:
return (AnimationSequence(frame_counter), log_entry)